package com.smdk.dsminio;
import cn.hutool.core.io.file.FileReader;
import com.smdk.dsminio.common.CommonVariable;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;

import javax.activation.MimetypesFileTypeMap;
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import static io.netty.handler.codec.http.HttpResponseStatus.OK;
import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1;


/**
 * @author 神秘的凯 Netty静态服务器 支持图片模糊 剪裁 旋转 缩放
 * @since 2020-04-08
 **/
public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
    //文件类型
    private static MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
    //请求Request
    private volatile FullHttpRequest request;

    @Override
    public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
        this.request = request;
        String requestUri = scannerURI(request.uri());
        File file;
        if (request.uri().length() > 0) {
            if (request.uri().equals("/favicon.ico")) {
                return;
            }
            if (requestUri.lastIndexOf("?") != -1) {
                file = FileHandler.ImagesHandler(requestUri);
            } else {
                file = new File(CommonVariable.ossStorageFilePath + requestUri);
            }
            //输出客户端
            DefaultFullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, OK, Unpooled.wrappedBuffer(new FileReader(file).readBytes()));
            //设置Headers
            response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
            //设置响应头
            setContentTypeHeader(response, file);
            //设置响应
            ChannelFuture lastContentFuture = ctx.writeAndFlush(response);
            //判断通道是否活跃
            final boolean keepAlive = HttpUtil.isKeepAlive(request);
            if (!keepAlive) {
                lastContentFuture.addListener(ChannelFutureListener.CLOSE);
                response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
            }
            return;
        }
    }

    /**
     * 异常处理
     * @param ctx
     * @param cause
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        cause.printStackTrace();
        if (ctx.channel().isActive()) {
            sendError(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR);
        }
    }


    private static String scannerURI(String uri) {
        // Decode the path.
        try {
            uri = URLDecoder.decode(uri, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
        if (uri.isEmpty() || uri.charAt(0) != '/') {
            return null;
        }
        uri = uri.replace('/', File.separatorChar);

        //去掉../root获取上级路径之类的危险操作
        if (uri.contains(File.separator + '.') || uri.contains('.' + File.separator) || uri.charAt(0) == '.' || uri.charAt(uri.length() - 1) == '.') {
            return null;
        }
        return uri;
    }

    /**
     * 出现异常
     * @param ctx
     * @param status
     */
    private void sendError(ChannelHandlerContext ctx, HttpResponseStatus status) {
        FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, status, Unpooled.copiedBuffer("Failure: " + status + "\r\n", CharsetUtil.UTF_8));
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, "text/plain; charset=UTF-8");

        HttpUtil.setContentLength(response, response.content().readableBytes());

        response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
        ctx.writeAndFlush(ctx);
        this.sendAndCleanupConnection(ctx, response);
    }


    public void setContentTypeHeader(HttpResponse response, File file) {
        response.headers().set(HttpHeaderNames.CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
    }

    /**
     * 关闭连接
     * If Keep-Alive is disabled, attaches "Connection: close" header to the response
     * and closes the connection after the response being sent.
     */
    private void sendAndCleanupConnection(ChannelHandlerContext ctx, FullHttpResponse response) {
        // ctx.re
        final FullHttpRequest request = this.request;
        final boolean keepAlive = HttpUtil.isKeepAlive(request);
        HttpUtil.setContentLength(response, response.content().readableBytes());
        response.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.CLOSE);
        ChannelFuture flushPromise = ctx.writeAndFlush(response);
        if (!keepAlive) {
            // Close the connection as soon as the response is sent.
            flushPromise.addListener(ChannelFutureListener.CLOSE);
        }
    }

    /**
     * 响应完毕
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.handlerRemoved(ctx);
        System.out.println(ctx.channel().remoteAddress() + "掉线");
    }

}